package org.moonlight.jvm.interpret;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.moonlight.jvm.classfile.MemberInfo;
import org.moonlight.jvm.classfile.attribute.CodeAttribute;
import org.moonlight.jvm.instructions.Factory;
import org.moonlight.jvm.instructions.base.BytecodeReader;
import org.moonlight.jvm.instructions.base.Instruction;
import org.moonlight.jvm.rtda.exclusive.Frame;
import org.moonlight.jvm.rtda.exclusive.Thread;
import org.moonlight.jvm.rtda.share.Object;
import org.moonlight.jvm.rtda.share.heap.Class;
import org.moonlight.jvm.rtda.share.heap.ClassLoader;
import org.moonlight.jvm.rtda.share.heap.classmember.Method;
import org.moonlight.jvm.rtda.share.methodarea.StringPool;

/**
 * 解释器
 *
 * @author Moonlight
 * @createTime 2023/8/23 10:54
 **/
public class Interpret {

    public void interpret(Method method, boolean logInst, String[] args) {
        Thread thread = new Thread();
        Frame frame = thread.newFrame(method);
        thread.pushFrame(frame);

        Object myArray = createArgsArray(method.getClazz().getLoader(), args);
        frame.getLocalVars().setRef(0, myArray);

        try {
            loop(thread, logInst);
        } catch (Exception e) {
            logFrames(thread);
            e.printStackTrace();
        }
    }

    public void interpret(Thread thread, boolean logInst) {
        try {
            loop(thread, logInst);
        } catch (Exception e) {
            logFrames(thread);
            e.printStackTrace();
        }
    }

    private Object createArgsArray(ClassLoader classLoader, String[] args) {
        Class javaStringClass = classLoader.loadClass("java/lang/String");
        Object myArray = javaStringClass.arrayClass().newArray(args.length);
        Object[] refs = myArray.refs();
        for (int i = 0; i < args.length; i++) {
            refs[i] = StringPool.myString(classLoader, args[i]);
        }
        return myArray;
    }

    private void loop(Thread thread, boolean logInst) {
        BytecodeReader reader = new BytecodeReader();
        Gson gson = new GsonBuilder().create();;

        while (true) {
            Frame frame = thread.currentFrame();
            // 计算pc
            int pc = frame.getNextPc();
            thread.setPc(pc);

            // 解码指令
            reader.reset(frame.getMethod().getCode(), pc);
            int opcode = reader.readOpCode();
            Instruction instruction = Factory.newInstruction(opcode);
            if (instruction == null) {
                System.out.println("指令尚未实现: " + opcode + ", " + Integer.toHexString(opcode));
                break;
            }
            instruction.fetchOperands(reader);
            frame.setNextPc(reader.getPc());

            if (logInst) {
                System.out.println(
                        "\r\n pc: " + pc
                        + "\r\n 指令码: " + opcode + ", " + Integer.toHexString(opcode) + ", " + instruction.getClass().getSimpleName()
                        + "\r\n 指令: " + gson.toJson(instruction));
            }
            // 执行指令
            instruction.execute(frame);

            if (thread.isStackEmpty()) {
                break;
            }
        }
    }

    private void logFrames(Thread thread) {
        while (!thread.isStackEmpty()) {
            Frame frame = thread.popFrame();
            Method method = frame.getMethod();
            String className = method.getClazz().getName();
            System.out.println(">> pc:" + frame.getNextPc() + " " + className + "." + method.getName() + method.getDescriptor() + "\n");
        }
    }

}